package com.yvon.boot.excel.converter;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.yvon.boot.excel.annotation.ExcelEnumFormat;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import static cn.hutool.core.util.ReflectUtil.invoke;
import static com.alibaba.excel.enums.CellDataTypeEnum.*;

/**
 * 枚举格式化转换处理
 *
 * @author Yvon
 */
@Slf4j
public class ExcelEnumConverter implements Converter<Object> {

	@Override
	public Class<Object> supportJavaTypeKey() {
		return Object.class;
	}

	@Override
	public CellDataTypeEnum supportExcelTypeKey() {
		return null;
	}

	@Override
	public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
		cellData.checkEmpty();
		// Excel中填入的是枚举中指定的描述
		Object textValue = null;
		if (cellData.getType().equals(STRING) || cellData.getType().equals(DIRECT_STRING) || cellData.getType().equals(RICH_TEXT_STRING)) {
			textValue = cellData.getStringValue();
		}
		if(cellData.getType().equals(NUMBER)) {
			textValue = cellData.getNumberValue();
		}
		if(cellData.getType().equals(BOOLEAN)) {
			textValue = cellData.getBooleanValue();
		}
		// 如果是空值
		if (ObjectUtil.isNull(textValue)) {
			return null;
		}
		Map<Object, String> enumCodeToTextMap = beforeConvert(contentProperty);
		// 从Java输出至Excel是code转text
		// 因此从Excel转Java应该将text与code对调
		Map<Object, Object> enumTextToCodeMap = new HashMap<>();
		enumCodeToTextMap.forEach((key, value) -> enumTextToCodeMap.put(value, key));
		// 应该从text -> code中查找
		Object codeValue = enumTextToCodeMap.get(textValue);
		return Convert.convert(contentProperty.getField().getType(), codeValue);
	}

	@Override
	public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
		if (ObjectUtil.isNull(object)) {
			return new WriteCellData<>("");
		}
		ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
		if (object instanceof Enum) {
			String value = invokeGetter(object, anno.textField());
			return new WriteCellData<>(value);
		}
		Map<Object, String> enumValueMap = beforeConvert(contentProperty);
		String value = Convert.toStr(enumValueMap.get(object), "");
		return new WriteCellData<>(value);
	}

	private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {
		ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
		Map<Object, String> enumValueMap = new HashMap<>();
		Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
		for (Enum<?> enumConstant : enumConstants) {
			Object codeValue = invokeGetter(enumConstant, anno.codeField());
			String textValue = invokeGetter(enumConstant, anno.textField());
			enumValueMap.put(codeValue, textValue);
		}
		return enumValueMap;
	}

	private ExcelEnumFormat getAnnotation(Field field) {
		return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class);
	}

	public <E> E invokeGetter(Object obj, String propertyName) {
		Object object = obj;
		for (String name : StrUtil.split(propertyName, ".")) {
			String getterMethodName = "get" + StringUtils.capitalize(name);
			object = invoke(object, getterMethodName);
		}
		return (E) object;
	}
}
